////////////////////////////////////////////////////////////////////////////////
//
//  FLEX USER GROUP HOKURIKU
//  Copyright 2009 Flex User Group Hokuriku
//  All Rights Reserved.
// 
//  NOTICE: Flex User Group Hokuriku permits you to use, 
//  modify, and distribute this file 
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package net.fxug.hokuriku.chocolat.service
{
    import flash.errors.IOError;
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.events.ServerSocketConnectEvent;
    import flash.net.ServerSocket;
    import flash.net.Socket;
    import flash.utils.getQualifiedClassName;
    
    import mx.logging.ILogger;
    
    import net.fxug.hokuriku.chocolat.events.VoteEvent;
    import net.fxug.hokuriku.chocolat.model.NetworkInterfaceData;
    import net.fxug.hokuriku.chocolat.model.VoteData;
    import net.fxug.hokuriku.chocolat.utils.LoggingUtil;

    /**
     *
     * @author mac
     */
    public class SocketServer implements IVoteWatcher
    {
        /** 自身のインターフェイスプロトコルを表現する文字列 */
        public static const WATCHER_TYPE:String = "lan";

        public static const PORT:int = 44445;

        private static const logger:ILogger = LoggingUtil.getLogger(SocketServer);

        private var sonarReceiver:SonarReceiver;
        private var serverSocket:ServerSocket;
        private var sockets:Vector.<Socket>;
        private var networkData:NetworkInterfaceData;

        /**
         *
         * @param inNetworkData
         * @throws IllegalOperationError
         */
        public function SocketServer(inNetworkData:NetworkInterfaceData)
        {
            if (inNetworkData is NetworkInterfaceData)
            {
                this.networkData = inNetworkData;
            }
            else
            {
                throw new IllegalOperationError("[Error] Illegal Constructor param error");
            }
            this.sockets = new Vector.<Socket>();
        }

        /**
         * @inheritDoc com.adobe.jp.smtimer.IVoteWatcher#getInterfaceName()
         */
        public function getInterfaceName():String
        {
            return WATCHER_TYPE;
        }

        /**
         *
         */
        public function start():void
        {
            this.sonarReceiver = new SonarReceiver(this.networkData.address);

            try
            {
                this.sonarReceiver.recieve();
                this.serverSocket = new ServerSocket();
            }
            catch (e:Error)
            {
                logger.error(e.message);
            }

            this.serverSocket = new ServerSocket();

            logger.info("Server Socket Listen Start port = {0} / address = {1}", PORT, this.networkData.address);

            this.serverSocket.addEventListener(Event.CONNECT, serverSocketConnectEventHandler);

            this.serverSocket.bind(PORT, this.networkData.address);
            this.serverSocket.listen();

            //FIXME
            // serverSocketが起動完了してリスン状態になったイベントで本来は以下を実行する必要がある。
            VoteManager.getInstance().onReady(this);
        }

        /**
         * リスン状態の場合にはtrueを返す。
         * @return サーバのリスン状態が有効な場合にtrue
         */
        public function isEnabled():Boolean
        {
            return (this.serverSocket && this.serverSocket.listening);
        }

        public function destroy():void
        {
            logger.info("SocketServer::destroy");

            try
            {
				this.sonarReceiver.close();
                this.serverSocket.close();
            }
            catch (error:IOError)
            {
                logger.error(error.message);
            }
        }

        public function toString():String
        {
            return getQualifiedClassName(SocketServer + ":" + WATCHER_TYPE);
        }

        private function serverSocketConnectEventHandler(event:ServerSocketConnectEvent):void
        {
            logger.debug("SocketServer::serverSocketConnectEventHandler");

            var socket:Socket = event.socket;
            logger.info("	connected = {0}", socket.connected);
            if (!socket.connected)
            {
                logger.info("    socket connected = false error");
            }

            socket.addEventListener(ProgressEvent.SOCKET_DATA, socketProgressEventHandler);
            this.sockets.push(socket);
        }

        private function socketProgressEventHandler(event:ProgressEvent):void
        {
            logger.debug("SocketServer::socketProgressEventHandler");
            var socket:Socket = event.target as Socket;
            var receivedStr:String = socket.readUTFBytes(socket.bytesAvailable)
            var receivedXML:XML;
            try
            {
                receivedXML = new XML(receivedStr);
            }
            catch (e:Error)
            {
                logger.error("XML Error::{0}", e.message);
            }

            if (receivedXML && (receivedXML.vote == VoteEvent.GOOD_EVENT || receivedXML.vote == VoteEvent.BAD_EVENT))
            {
                logger.debug("    dispath voteEvent");

                receivedXML.message = String(receivedXML.message).replace("&gt;", "<");
                receivedXML.message = String(receivedXML.message).replace("&lt;", ">");

                logger.info("    vote = {0}", receivedXML.vote);
                logger.info("    message = {0}", receivedXML.message);

                var voteData:VoteData = new VoteData();
                voteData.id = receivedXML.user_name;
                voteData.text = receivedXML.message;
                voteData.voteEnabled = receivedXML.voteEnabled;
                voteData.route = SocketServer.WATCHER_TYPE;
                voteData.datetime = new Date();

                var voteEvent:VoteEvent = new VoteEvent(receivedXML.vote, [voteData]);
                VoteManager.getInstance().dispatchVote(voteEvent);
            }

            socket.close();
            this.sockets.splice(this.sockets.indexOf(socket), 1);
        }
    }
}